#include "erl_nif.h"
#include "stack_pool.h"

// _MSC_VER
#ifdef _MSC_VER
#include "Windows.h"
#define ATOMIC_CMPXCHG(PTR, NEW, OLD) InterlockedCompareExchange(PTR, NEW, OLD)
#endif

// __GNUC__
#ifdef __GNUC__
#define ATOMIC_CMPXCHG(PTR, NEW, OLD) __sync_val_compare_and_swap(PTR, OLD, NEW)
#endif

#define TRUE 1
#define FALSE 0

FG_LFPOOL* fg_lfpool_new()
{
    FG_LFPOOL* pool = (FG_LFPOOL*)enif_alloc(sizeof(FG_LFPOOL));
    pool->head = NULL;
    pool->size = 0;
    return pool;
}

int fg_lfpool_opt_size()
{
    ErlNifSysInfo sip;
    enif_system_info(&sip, sizeof(ErlDrvSysInfo));
    return sip.scheduler_threads;
}

int fg_lfpool_add(FG_LFPOOL* pool, void* data)
{
    FG_LFNODE* node;
    if(NULL == data)
    {
        return FALSE;
    }

    node = (FG_LFNODE*)enif_alloc(sizeof(FG_LFNODE));
    if(NULL == node)
    {
        return FALSE;
    }
    node->in_use = 0;
    if(NULL == pool->head)
    {
        node->next = node;
        pool->head = node;
    }
    else
    {
        node->next = pool->head->next;
        pool->head->next = node;
    }
    node->data = data;
    node->id = pool->size;
    pool->size += 1;

    return TRUE;
}

/*
从池中取出一个元素并标记为使用中, 注意使用完必须用 fg_lfpool_putback 放回
此函数是线程安全的
 */
FG_LFNODE* fg_lfpool_take(FG_LFPOOL* pool)
{
    FG_LFNODE* node;
    node = pool->head;
    if(NULL == node)
    {
        return NULL;
    }
    while(ATOMIC_CMPXCHG(&(node->in_use), 1, 0))
    {
        node = node->next;
    }
    // pool->head = node->next;
    return node;
}

/*
把一个元素标记为未使用(即放回池中)
 */
void fg_lfpool_putback(FG_LFNODE* node)
{
    node->in_use = 0;
}


FG_LFNODE* fg_lfpool_first(FG_LFPOOL* pool)
{
    return pool->head;
}

FG_LFNODE* fg_lfpool_next(FG_LFNODE* node)
{
    return node->next;
}

int fg_lfpool_size(FG_LFPOOL* pool)
{
    return pool->size;
}

/*
从池中取出一个数据并标记为使用中, 注意用完用必须用 fg_lfpool_putback_data 放回
此函数是线程安全的
 */
void* fg_lfpool_take_data(FG_LFPOOL* pool)
{
    FG_LFNODE* node;
    node = fg_lfpool_take(pool);
    if(NULL == node)
    {
        return NULL;
    }
    else
    {
        return node->data;
    }
}

/*
把一个数据放回池中
 */
int fg_lfpool_putback_data(FG_LFPOOL* pool, void* data)
{
    FG_LFNODE* node;
    int i;

    node = pool->head;
    for(i=0; i<pool->size; i++)
    {
        if(data == node->data)
        {
            fg_lfpool_putback(node);
            return TRUE;
        }
        else
        {
            node = node->next;
        }
    }
    return FALSE;
}